package rexsee.cartoon;

import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;

import rexsee.core.browser.Browser;
import android.view.GestureDetector;
import android.view.GestureDetector.OnDoubleTapListener;
import android.view.GestureDetector.OnGestureListener;
import android.view.MotionEvent;
import android.view.View;
import android.view.View.OnTouchListener;

public class CartoonViewTouchListener implements OnTouchListener {

	public static final String EVENT_ONCLICK = "onCartoonClick";
	public static final String EVENT_ONDOUBLECLICK = "onCartoonDoubleClick";
	public static final String EVENT_ONFLING = "onCartoonFling";
	public static final String EVENT_ONLONGPRESS = "onCartoonLongPress";
	public static final String EVENT_ONTOUCHDOWN = "onCartoonTouchDown";
	public static final String EVENT_ONTOUCHUP = "onCartoonTouchUp";
	public static final String EVENT_ONTOUCHMOVE = "onCartoonTouchMove";
	public static final String EVENT_ONTOUCHCANCEL = "onCartoonTouchCancel";

	private final Browser mBrowser;
	private boolean listenToTouch = false;
	private boolean listenToTouchMove = false;
	public final ArrayList<CartoonTouchListener> listeners = new ArrayList<CartoonTouchListener>();

	private final int canvasWidth;
	private final int canvasHeight;

	private View currentView = null;

	public CartoonViewTouchListener(Browser browser, boolean listenToTouch, boolean listenToTouchMove, int canvasWidth, int canvasHeight) {

		mBrowser = browser;
		this.listenToTouch = listenToTouch;
		this.listenToTouchMove = listenToTouchMove;

		this.canvasWidth = canvasWidth;
		this.canvasHeight = canvasHeight;

		if (listenToTouch) {
			mBrowser.eventList.add(EVENT_ONTOUCHDOWN);
			mBrowser.eventList.add(EVENT_ONTOUCHUP);
			mBrowser.eventList.add(EVENT_ONTOUCHMOVE);
			mBrowser.eventList.add(EVENT_ONTOUCHCANCEL);
			mBrowser.eventList.add(EVENT_ONDOUBLECLICK);
			mBrowser.eventList.add(EVENT_ONLONGPRESS);
			mBrowser.eventList.add(EVENT_ONFLING);
			mBrowser.eventList.add(EVENT_ONCLICK);
		}

	}

	private final GestureDetector mGestureDetector = new GestureDetector(new CartoonViewGestureListener());

	private int scaleX(float pixel) {
		if (currentView == null) return (int) pixel;
		float xScale = (float) canvasWidth / (float) currentView.getWidth();
		return Math.round(pixel * xScale);
	}
	private int scaleY(float pixel) {
		if (currentView == null) return (int) pixel;
		float yScale = (float) canvasHeight / (float) currentView.getHeight();
		return Math.round(pixel * yScale);
	}

	@Override
	public boolean onTouch(View v, MotionEvent event) {
		currentView = v;
		int action = event.getAction();
		int x = scaleX(event.getX());
		int y = scaleY(event.getY());
		String[] argu = new String[]{"", "", "", String.valueOf(x), String.valueOf(y)};
		if (action == MotionEvent.ACTION_DOWN) {
			runListeners(EVENT_ONTOUCHDOWN, event);
			if (listenToTouch) mBrowser.eventList.run(EVENT_ONTOUCHDOWN, argu);
		} else if (action == MotionEvent.ACTION_UP) {
			runListeners(EVENT_ONTOUCHUP, event);
			if (listenToTouch) mBrowser.eventList.run(EVENT_ONTOUCHUP, argu);
		} else if (action == MotionEvent.ACTION_MOVE) {
			runListeners(EVENT_ONTOUCHMOVE, event);
			if (listenToTouch && listenToTouchMove) mBrowser.eventList.run(EVENT_ONTOUCHMOVE, argu);
		} else if (action == MotionEvent.ACTION_CANCEL) {
			runListeners(EVENT_ONTOUCHCANCEL, event);
			if (listenToTouch && listenToTouchMove) mBrowser.eventList.run(EVENT_ONTOUCHCANCEL, argu);
		}
		return mGestureDetector.onTouchEvent(event);
	}

	private class CartoonViewGestureListener
			implements
			OnDoubleTapListener,
			OnGestureListener {

		@Override
		public boolean onDoubleTap(MotionEvent e) {
			runListeners(EVENT_ONDOUBLECLICK, e);
			if (listenToTouch) {
				int x = scaleX(e.getX());
				int y = scaleY(e.getY());
				mBrowser.eventList.run(EVENT_ONDOUBLECLICK, new String[]{"", "", "", String.valueOf(x), String.valueOf(y)});
			}
			return false;
		}
		@Override
		public boolean onDoubleTapEvent(MotionEvent e) {
			return false;
		}
		@Override
		public boolean onSingleTapConfirmed(MotionEvent e) {
			return false;
		}
		@Override
		public boolean onDown(MotionEvent e) {
			return false;
		}
		@Override
		public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY) {
			runListeners(EVENT_ONFLING, e1, e2, velocityX, velocityY);
			if (listenToTouch) {
				int x1 = scaleX(e1.getX());
				int y1 = scaleY(e1.getY());
				int x2 = scaleX(e2.getX());
				int y2 = scaleY(e2.getY());
				long x3 = e2.getDownTime();
				long y3 = e2.getEventTime();
				float x4 = scaleX(velocityX);
				float y4 = scaleY(velocityY);
				String[] argu = new String[]{
						"",
						String.valueOf(x1),
						String.valueOf(y1),
						String.valueOf(x2),
						String.valueOf(y2),
						String.valueOf(x3),
						String.valueOf(y3),
						String.valueOf(x4),
						String.valueOf(y4)
				};
				mBrowser.eventList.run(EVENT_ONFLING, argu);
			}
			return false;
		}
		@Override
		public void onLongPress(MotionEvent e) {
			runListeners(EVENT_ONLONGPRESS, e);
			if (listenToTouch) {
				int x = scaleX(e.getX());
				int y = scaleY(e.getY());
				mBrowser.eventList.run(EVENT_ONLONGPRESS, new String[]{"", "", "", String.valueOf(x), String.valueOf(y)});
			}
		}
		@Override
		public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY) {
			return false;
		}
		@Override
		public void onShowPress(MotionEvent e) {
		}
		@Override
		public boolean onSingleTapUp(MotionEvent e) {
			runListeners(EVENT_ONCLICK, e);
			if (listenToTouch) {
				int x = scaleX(e.getX());
				int y = scaleY(e.getY());
				mBrowser.eventList.run(EVENT_ONCLICK, new String[]{"", "", "", String.valueOf(x), String.valueOf(y)});
			}
			return false;
		}

	}

	public void registerTouchListener(CartoonTouchListener listener) {
		if (listener != null && !listeners.contains(listener)) listeners.add(listener);
	}
	private void runListeners(String action, MotionEvent event) {
		runListeners(action, event, null, -1, -1);
	}
	private void runListeners(String action, MotionEvent event, MotionEvent event2, float velocityX, float velocityY) {
		TouchEvent e = new TouchEvent();
		e.action = action;
		e.x = scaleX(event.getX());
		e.y = scaleY(event.getY());
		if (event2 != null) {
			e.x2 = scaleX(event2.getX());
			e.y2 = scaleX(event2.getY());
		}
		e.downTime = (event2 == null) ? event.getDownTime() : event2.getDownTime();
		e.eventTime = (event2 == null) ? event.getEventTime() : event2.getEventTime();
		e.velocityX = velocityX;
		e.velocityY = velocityY;
		TouchEvent cancel = e.copy(EVENT_ONTOUCHCANCEL);
		Collections.sort(listeners, new CompratorForCartoonTouchListener());
		boolean blocked = false;
		for (int i = listeners.size() - 1; i >= 0; i--) {
			if (blocked) {
				listeners.get(i).onEvent(cancel);
			} else {
				blocked = (listeners.get(i).onEvent(e));
			}
		}
	}

	public static class TouchEvent {
		public String action;
		public int x;
		public int y;
		public int x2;
		public int y2;
		public long downTime;
		public long eventTime;
		public float velocityX;
		public float velocityY;
		public TouchEvent copy(String newAction) {
			TouchEvent rtn = new TouchEvent();
			rtn.action = newAction;
			rtn.x = x;
			rtn.y = y;
			rtn.x2 = x2;
			rtn.y2 = y2;
			rtn.downTime = downTime;
			rtn.eventTime = eventTime;
			rtn.velocityX = velocityX;
			rtn.velocityY = velocityY;
			return rtn;
		}
	}

	public static abstract class CartoonTouchListener {
		public abstract boolean onEvent(TouchEvent touchEvent);
		public abstract int getZIndex();
	}

	public static class CompratorForCartoonTouchListener
			implements
			Comparator<CartoonTouchListener> {
		@Override
		public int compare(CartoonTouchListener listener1, CartoonTouchListener listener2) {
			long diff = listener1.getZIndex() - listener2.getZIndex();
			if (diff > 0) return 1;
			else if (diff == 0) return 0;
			else return -1;
		}
		@Override
		public boolean equals(Object obj) {
			return true;
		}
	}

}
